package ru.budget.mobile.ui.base

import androidx.lifecycle.ViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import ru.budget.mobile.core.Response
import ru.budget.mobile.core.ResponseStates
import ru.budget.mobile.domain.usecase.consumption.AddConsumptionUseCase
import ru.budget.mobile.domain.usecase.consumption.GetConsumptionListUseCase
import ru.budget.mobile.domain.usecase.income.AddIncomeUseCase
import ru.budget.mobile.domain.usecase.income.GetIncomeListUseCase
import ru.budget.mobile.ui.entities.ConsumptionUI
import ru.budget.mobile.ui.entities.IncomeUI
import ru.budget.mobile.ui.entities.TimeRange
import ru.budget.mobile.ui.entities.states.BudgetState
import ru.budget.mobile.ui.entities.states.ConsumptionState
import ru.budget.mobile.ui.entities.states.IncomeState
import java.time.LocalDate

class BudgetViewModel(
    private val getIncomeListUseCase: GetIncomeListUseCase,
    private val addIncomeUseCase: AddIncomeUseCase,
    private val getConsumptionListUseCase: GetConsumptionListUseCase,
    private val addConsumptionUseCase: AddConsumptionUseCase
): ViewModel() {

    private val _stateFlow = MutableStateFlow(BudgetState())
    val stateFlow = _stateFlow.asStateFlow()

    init {
        loadIncomeList()
        loadConsumptionList()
        check()
        dikson()
    }

    private fun balance(one: Double, two: Double) = one - two

    private fun sumIncome(type: TimeRange, list: List<IncomeUI>): Double {
        val formatter = DateTimeFormatter()
        val today = formatter.setDateAndTime(System.currentTimeMillis())
        val week = ((today.day.toInt()).toString()..(today.day.toInt() + 6).toString())
        val month = today.month

        return when(type) {
            TimeRange.TODAY -> {
                list.filter {
                    it.datetime == today
                }.sumOf {
                    it.sum
                }
            }
            TimeRange.WEEK -> {
                list.filter {
                    week.contains(it.datetime.day)
                }.sumOf {
                    it.sum
                }
            }
            TimeRange.MONTH -> {
                list.filter {
                    it.datetime.month == month
                }.sumOf {
                    it.sum
                }
            }
        }
    }

    private fun sumConsumption(type: TimeRange, list: List<ConsumptionUI>): Double {
        val formatter = DateTimeFormatter()
        val today = formatter.setDateAndTime(System.currentTimeMillis())
        val week = ((today.day.toInt()).toString()..(today.day.toInt() + 6).toString())
        val month = today.month

        return when(type) {
            TimeRange.TODAY -> {
                list.filter {
                    it.datetime == today
                }.sumOf {
                    it.sum
                }
            }
            TimeRange.WEEK -> {
                list.filter {
                    week.contains(it.datetime.day)
                }.sumOf {
                    it.sum
                }
            }
            TimeRange.MONTH -> {
                list.filter {
                    it.datetime.month == month
                }.sumOf {
                    it.sum
                }
            }
        }
    }

    private fun dikson() {
        CoroutineScope(Dispatchers.IO).launch {
            _stateFlow.update { state ->
                state.copy(
                    incomeState = IncomeState(
                        sumToday = sumIncome(
                            type = TimeRange.TODAY,
                            list = state.incomeState.incomeList
                        ),
                        sumWeek = sumIncome(
                            type = TimeRange.WEEK,
                            list = state.incomeState.incomeList
                        ),
                        sumMonth = sumIncome(
                            type = TimeRange.MONTH,
                            list = state.incomeState.incomeList
                        )
                    ),
                    consumptionState = ConsumptionState(
                        sumToday = sumConsumption(
                            type = TimeRange.TODAY,
                            list = state.consumptionState.consumptionList
                        ),
                        sumWeek = sumConsumption(
                            type = TimeRange.WEEK,
                            list = state.consumptionState.consumptionList
                        ),
                        sumMonth = sumConsumption(
                            type = TimeRange.MONTH,
                            list = state.consumptionState.consumptionList
                        )
                    )
                )
            }
        }
    }

    private fun check() {
        CoroutineScope(Dispatchers.IO).launch {
            _stateFlow.update { state ->
                state.copy(
                    balance = balance(
                        one = state.incomeState.incomeList.sumOf { it.sum },
                        two = state.consumptionState.consumptionList.sumOf { it.sum }
                    )
                )
            }
        }
    }

    private fun loadIncomeList() {
        CoroutineScope(Dispatchers.IO).launch {
            getIncomeListUseCase().collect { result ->
                when(result) {
                    is Response.Loading -> _stateFlow.update {
                        it.copy(
                            incomeState = IncomeState(
                                state = result.state
                            )
                        )
                    }

                    is Response.Success -> _stateFlow.update {
                        it.copy(
                            incomeState = IncomeState(
                                incomeList = result.data,
                                state = result.state
                            ),
                        )
                    }

                    is Response.Error -> _stateFlow.update {
                        it.copy(
                            incomeState = IncomeState(
                                state = result.state
                            )
                        )
                    }
                }
            }
        }
    }

    fun addIncome(income: IncomeUI): Boolean {
        CoroutineScope(Dispatchers.IO).launch {
            addIncomeUseCase(income).collect { result ->
                when(result) {
                    is Response.Loading -> _stateFlow.update {
                        it.copy(
                            incomeState = IncomeState(
                                stateAdd = result.state
                            )
                        )
                    }

                    is Response.Success -> _stateFlow.update {
                        it.copy(
                            incomeState = IncomeState(
                                stateAdd = result.state
                            )
                        )
                    }

                    is Response.Error -> _stateFlow.update {
                        it.copy(
                            incomeState = IncomeState(
                                stateAdd = result.state
                            )
                        )

                    }
                }
            }
        }
        return _stateFlow.value.incomeState.stateAdd == ResponseStates.SUCCESS
    }

    private fun loadConsumptionList() {
        CoroutineScope(Dispatchers.IO).launch {
            getConsumptionListUseCase().collect { result ->
                when(result) {
                    is Response.Loading -> _stateFlow.update {
                        it.copy(
                            consumptionState = ConsumptionState(
                                state = result.state
                            )
                        )
                    }

                    is Response.Success -> _stateFlow.update {
                        it.copy(
                            consumptionState = ConsumptionState(
                                consumptionList = result.data,
                                state = result.state
                            ),
                        )
                    }

                    is Response.Error -> _stateFlow.update {
                        it.copy(
                            consumptionState = ConsumptionState(
                                state = result.state
                            )
                        )
                    }
                }
            }
        }
    }

    fun addConsumption(consumption: ConsumptionUI): Boolean {
        CoroutineScope(Dispatchers.IO).launch {
            addConsumptionUseCase(consumption).collect { result ->
                when(result) {
                    is Response.Loading -> _stateFlow.update {
                        it.copy(
                            consumptionState = ConsumptionState(
                                stateAdd = result.state
                            )
                        )
                    }

                    is Response.Success -> _stateFlow.update {
                        it.copy(
                            consumptionState = ConsumptionState(
                                stateAdd = result.state
                            )
                        )
                    }

                    is Response.Error -> _stateFlow.update {
                        it.copy(
                            consumptionState = ConsumptionState(
                                stateAdd = result.state
                            )
                        )
                    }
                }
            }
        }
        return _stateFlow.value.consumptionState.stateAdd == ResponseStates.SUCCESS
    }
}
